3 # The Dictionary class is a Hash that preserves order.
 
   4 # So it has some array-like extensions also. By defualt
 
   5 # a Dictionary object preserves insertion order, but any
 
   6 # order can be specified including alphabetical key order.
 
  10 # Just require this file and use Dictionary instead of Hash.
 
  13 #   hsh = Dictionary.new
 
  17 #   p hsh.keys     #=> ['z','a','c']
 
  19 #   # or using Dictionary[] method
 
  20 #   hsh = Dictionary['z', 1, 'a', 2, 'c', 3]
 
  21 #   p hsh.keys     #=> ['z','a','c']
 
  23 #   # but this don't preserve order
 
  24 #   hsh = Dictionary['z'=>1, 'a'=>2, 'c'=>3]
 
  25 #   p hsh.keys     #=> ['a','c','z']
 
  27 #   # Dictionary has useful extensions: push, pop and unshift
 
  28 #   p hsh.push('to_end', 15)       #=> true, key added
 
  29 #   p hsh.push('to_end', 30)       #=> false, already - nothing happen
 
  30 #   p hsh.unshift('to_begin', 50)  #=> true, key added
 
  31 #   p hsh.unshift('to_begin', 60)  #=> false, already - nothing happen
 
  32 #   p hsh.keys                     #=> ["to_begin", "a", "c", "z", "to_end"]
 
  33 #   p hsh.pop                      #=> ["to_end", 15], if nothing remains, return nil
 
  34 #   p hsh.keys                     #=> ["to_begin", "a", "c", "z"]
 
  35 #   p hsh.shift                    #=> ["to_begin", 30], if nothing remains, return nil
 
  39 # * You can use #order_by to set internal sort order.
 
  40 # * #<< takes a two element [k,v] array and inserts.
 
  41 # * Use ::auto which creates Dictionay sub-entries as needed.
 
  42 # * And ::alpha which creates a new Dictionary sorted by key.
 
  51 # * Andrew Johnson (merge, to_a, inspect, shift and Hash[])
 
  52 # * Jeff Sharpe    (reverse and reverse!)
 
  53 # * Thomas Leitner (has_key? and key?)
 
  55 # Originally ported from OrderHash 2.0, Copyright (c) 2005 jan molic
 
  60 # ** Fixed initialize so the constructor blocks correctly effected dictionary rather then just the internal hash.
 
  64 # Copyright (c) 2005 Jan Molic, Thomas Sawyer
 
  68 # This module is free software. You may use, modify, and/or redistribute this
 
  69 # software under the same terms as Ruby.
 
  71 # This program is distributed in the hope that it will be useful, but WITHOUT
 
  72 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 
  73 # FOR A PARTICULAR PURPOSE.
 
  78 # The Dictionary class is a Hash that preserves order.
 
  79 # So it has some array-like extensions also. By defualt
 
  80 # a Dictionary object preserves insertion order, but any
 
  81 # order can be specified including alphabetical key order.
 
  85 # Just require this file and use Dictionary instead of Hash.
 
  88 #   hsh = Dictionary.new
 
  92 #   p hsh.keys     #=> ['z','a','c']
 
  94 #   # or using Dictionary[] method
 
  95 #   hsh = Dictionary['z', 1, 'a', 2, 'c', 3]
 
  96 #   p hsh.keys     #=> ['z','a','c']
 
  98 #   # but this don't preserve order
 
  99 #   hsh = Dictionary['z'=>1, 'a'=>2, 'c'=>3]
 
 100 #   p hsh.keys     #=> ['a','c','z']
 
 102 #   # Dictionary has useful extensions: push, pop and unshift
 
 103 #   p hsh.push('to_end', 15)       #=> true, key added
 
 104 #   p hsh.push('to_end', 30)       #=> false, already - nothing happen
 
 105 #   p hsh.unshift('to_begin', 50)  #=> true, key added
 
 106 #   p hsh.unshift('to_begin', 60)  #=> false, already - nothing happen
 
 107 #   p hsh.keys                     #=> ["to_begin", "a", "c", "z", "to_end"]
 
 108 #   p hsh.pop                      #=> ["to_end", 15], if nothing remains, return nil
 
 109 #   p hsh.keys                     #=> ["to_begin", "a", "c", "z"]
 
 110 #   p hsh.shift                    #=> ["to_begin", 30], if nothing remains, return nil
 
 114 # * You can use #order_by to set internal sort order.
 
 115 # * #<< takes a two element [k,v] array and inserts.
 
 116 # * Use ::auto which creates Dictionay sub-entries as needed.
 
 117 # * And ::alpha which creates a new Dictionary sorted by key.
 
 125     # TODO is this needed? Doesn't the super class do this?
 
 132       elsif (args.size % 2) != 0
 
 133         raise ArgumentError, "odd number of elements for Hash"
 
 136           hsh[args.shift] = args.shift
 
 142     # Like #new but the block sets the order.
 
 144     def new_by(*args, &blk)
 
 145       new(*args).order_by(&blk)
 
 148     # Alternate to #new which creates a dictionary sorted by key.
 
 150     #   d = Dictionary.alpha
 
 154     #   d  #=> {"x"=>3,"y"=>2,"z"=>2}
 
 156     # This is equivalent to:
 
 158     #   Dictionary.new.order_by { |key,value| key }
 
 160     def alpha(*args, &block)
 
 161       new(*args, &block).order_by_key
 
 164     # Alternate to #new which auto-creates sub-dictionaries as needed.
 
 166     #   d = Dictionary.auto
 
 167     #   d["a"]["b"]["c"] = "abc"  #=> { "a"=>{"b"=>{"c"=>"abc"}}}
 
 170       #AutoDictionary.new(*args)
 
 171       leet = lambda { |hsh, key| hsh[key] = new(&leet) }
 
 178   def initialize(*args, &blk)
 
 182       dict = self                                  # This ensure autmatic key entry effect the
 
 183       oblk = lambda{ |hsh, key| blk[dict,key] }    # dictionary rather then just the interal hash.
 
 184       @hash = Hash.new(*args, &oblk)
 
 186       @hash = Hash.new(*args)
 
 195   # Keep dictionary sorted by a specific sort order.
 
 197   def order_by( &block )
 
 203   # Keep dictionary sorted by key.
 
 205   #   d = Dictionary.new.order_by_key
 
 209   #   d  #=> {"x"=>3,"y"=>2,"z"=>2}
 
 211   # This is equivalent to:
 
 213   #   Dictionary.new.order_by { |key,value| key }
 
 215   # The initializer Dictionary#alpha also provides this.
 
 218     @order_by = lambda { |k,v| k }
 
 223   # Keep dictionary sorted by value.
 
 225   #   d = Dictionary.new.order_by_value
 
 229   #   d  #=> {"x"=>3,"y"=>2,"z"=>2}
 
 231   # This is equivalent to:
 
 233   #   Dictionary.new.order_by { |key,value| value }
 
 236     @order_by = lambda { |k,v| v }
 
 245       assoc = @order.collect{ |k| [k,@hash[k]] }.sort_by(&@order_by)
 
 246       @order = assoc.collect{ |k,v| k }
 
 252   #  return false if @order != hsh2.order
 
 257     if hsh2.is_a?( Dictionary )
 
 258       @order == hsh2.order &&
 
 259       @hash  == hsh2.instance_variable_get("@hash")
 
 270     @hash.fetch(k, *a, &b)
 
 277   # Or with additional index.
 
 279   #  h[key,index] = value
 
 281   def []=(k, i=nil, v=nil)
 
 295     @order.push( a ) unless @hash.has_key?( a )
 
 310     order.each { |k| yield( k ) }
 
 315     order.each { |k| yield( @hash[k] ) }
 
 320     order.each { |k| yield( k,@hash[k] ) }
 
 326     order.clone.each { |k| delete k if yield(k,@hash[k]) }
 
 332     order.each { |k| ary.push @hash[k] }
 
 341     hsh2 = self.class.new
 
 342     order.each { |k| hsh2[@hash[k]] = k }
 
 347     self.dup.delete_if(&block)
 
 350   def reject!( &block )
 
 351     hsh2 = reject(&block)
 
 352     self == hsh2 ? nil : hsh2
 
 362     key ? [key,delete(key)] : super
 
 366     unless @hash.include?( k )
 
 380     unless @hash.include?( k )
 
 391     key ? [key,delete(key)] : nil
 
 396     each {|k,v| ary << k.inspect + "=>" + v.inspect}
 
 397     '{' + ary.join(", ") + '}'
 
 402     each{ |k,v| a << k; a << v }
 
 407     hsh2.each { |k,v| self[k] = v }
 
 414     self.dup.update(hsh2)
 
 419     each { |k,v| ary << [k,v] if yield k,v }
 
 434     return @hash[order.first] unless x
 
 435     order.first(x).collect { |k| @hash[k] }
 
 440     return @hash[order.last] unless x
 
 441     order.last(x).collect { |k| @hash[k] }
 
 463     each { |k,v| ary << [k,v] }